// Copyright(c) 2011 Matthew McMullan, Jorel Lalicki and Mike Heise, Jorel Lalicki and Mike Heise. See copying.txt for details.

#ifndef __agml_v128_x86_h__
#define __agml_v128_x86_h__

// make sure that this file is never included directly
#ifndef __agml_agml_h__
#error "Include AGML from agml.h only. This file is not a valid entry point for the library."
#endif

#include <cmath>	// fixme

namespace agml {
	// Define primitive SIMD types
	/*! A 128-bit SIMD value designed for use with SSE commands */
	typedef __m128			_v128;
	typedef union{_v128 v; u64 data[2];} __v128_comparator;
	super_inline bool operator==(const __v128_comparator &a, const __v128_comparator &b) {
		return a.data[0]==b.data[0] && a.data[1]==b.data[1];
	}
	super_inline bool operator!=(const __v128_comparator &a, const __v128_comparator &b) {
		return a.data[0]!=b.data[0] || a.data[1]!=b.data[1];
	}
	struct throwaway {};
	/*! A 128-bit SIMD type designed for easy use with SSE commands */
	// u8/i8 u16/i16 u32/i32 u64/i64 f32/f64
	template<typename t> struct alignat(16) v128 {
		#define NUM (16/sizeof(t))
		#define TTYPE v128
		union {
			_v128 simd;
			t data[16/sizeof(t)];
		};
		// set functions are workarounds to allow this type to be used in unions
		super_inline void set(const v128 &o) {
			for (u32_fast i = 0; i<16/sizeof(t); ++i) {
				data[i] = o.data[i];
			}
		}
		super_inline void set(const t o) {
			for (u32_fast i = 0; i<16/sizeof(t); ++i) {
				data[i] = o;
			}
		}
		super_inline u32 size() const {
			return NUM;
		}
		super_inline bool operator==(const TTYPE &v) const {
			return simd==v.simd;
		}
		super_inline bool operator!=(const TTYPE &v) const {
			return simd!=v.simd;
		}
		super_inline t& operator[](const u32_fast i) {
			return data[i];
		}
		super_inline const t& operator[](const u32_fast i) const {
			return data[i];
		}
		super_inline void operator+=(const TTYPE &v) {
			for (u8 i=0; i<NUM; ++i) {
				data[i]+=v.data[i];
			}
		}
		super_inline void add(const TTYPE &v, TTYPE &dest) {
			for (u8 i=0; i<NUM; ++i) {
				dest.data[i] = data[i] + v.data[i];
			}
		}
		super_inline void sub(const TTYPE &v, TTYPE &dest) {
			for (u8 i=0; i<NUM; ++i) {
				dest.data[i] = data[i] - v.data[i];
			}
		}
		super_inline void mult(const TTYPE &v, TTYPE &dest) {
			for (u8 i=0; i<NUM; ++i) {
				dest.data[i] = data[i] * v.data[i];
			}
		}
		super_inline void dif(const TTYPE &v, TTYPE &dest) {
			for (u8 i=0; i<NUM; ++i) {
				dest.data[i] = data[i] / v.data[i];
			}
		}
		super_inline void operator++(int v) {
			for (u8 i=0; i<NUM; ++i) {
				data[i]++;
			}
		}
		super_inline void operator-=(const TTYPE &v) {
			for (u8 i=0; i<NUM; ++i) {
				data[i]-=v.data[i];
			}
		}
		super_inline void operator--(int v) {
			for (u8 i=0; i<NUM; ++i) {
				data[i]++;
			}
		}
		super_inline void operator*=(const TTYPE &v) {
			for (u8 i=0; i<NUM; ++i) {
				data[i]*=v.data[i];
			}
		}
		super_inline void operator/=(const TTYPE &v) {
			for (u8 i=0; i<NUM; ++i) {
				data[i]/=v.data[i];
			}
		}
		super_inline void operator%=(const TTYPE &v) {
			for (u8 i=0; i<NUM; ++i) {
				data[i]%=v.data[i];
			}
		}
		#undef TTYPE
		#undef NUM
	};
}

#endif
